import sys
from collections import OrderedDict
import os
import dlib
import glob
from skimage import io
import numpy as np
import cv2


#定义一组点的性质，即每个点属于的五官
FACIAL_LANDMARKS_68_IDXS = OrderedDict([
    ('forehead',(69,80)),
    ('mouth', (48, 68)),
    ('right_eyebrow', (17, 22)),
    ('left_eyebrow', (22, 27)),
    ('right_eye', (36, 42)),
    ('left_eye', (42, 48)),
    ('nose', (27, 36)),
    ('jaw', (0, 17))
])

#显示五官或脸部轮廓（连线）
def visualize_facial_landmarks(image, shape, colors=None, alpha=0.75):
    # 创建两个copy
    # overlay and one for the final output image
    overlay = image.copy()
    output = image.copy()
    # 设置一些颜色区域
    if colors is None:
        colors = [(19, 199, 109), (79, 76, 240), (230, 159, 23),
                  (168, 100, 168), (158, 163, 32),
                  (163, 38, 32), (180, 42, 220),(132,425,234)]

    for (i, name) in enumerate(FACIAL_LANDMARKS_68_IDXS.keys()):
        # 得到每一个点的坐标
        (j, k) = FACIAL_LANDMARKS_68_IDXS[name]
        pts = shape[j:k]
        if name == 'jaw' :
            # 用线条连起来
            for l in range(1, len(pts)):
                ptA = tuple(pts[l-1])
                ptB = tuple(pts[l])
                # 对位置进行连线
                cv2.line(overlay, ptA, ptB, colors[i], 2)
        elif name=='forehead':
            hull = cv2.convexHull(pts)
            cv2.drawContours(overlay, [hull], -1, colors[i], -1)
            # for l in range(0,len(pts)-3):
            #     ptA = tuple(pts[l-1])
            #     ptB = tuple(pts[l])
            #     # 对位置进行连线
            #     cv2.line(overlay, ptA, ptB, colors[i], 2)
        else:
            # 使用cv2.convexHull获得位置的凸包位置
            hull = cv2.convexHull(pts)
            # 使用cv2.drawContours画出轮廓图
            cv2.drawContours(overlay, [hull], -1, colors[i], -1)

    cv2.addWeighted(overlay, alpha, output, 1-alpha, 0, output)
    return output

#将shape变成np以便进行计算
#https://www.cnblogs.com/my-love-is-python/p/10463352.html
def shape_to_np(shape, dtype='int'):
    # 创建68*2用于存放坐标
    coords = np.zeros((shape.num_parts, 2), dtype=dtype)
    # 遍历每一个关键点
    # 得到坐标
    for i in range(0, shape.num_parts):
        coords[i] = (shape.part(i).x, shape.part(i).y)

    return coords



#读取视频并利用训练好的模型对其进行面部特征检测，共81个特征点
#https://github.com/codeniko/shape_predictor_81_face_landmarks

cap = cv2.VideoCapture('ayan_new.avi')
fourcc = cv2.VideoWriter_fourcc(*'DIVX')

# out = cv2.VideoWriter('ikun_1.mp4',fourcc, 20.0, (640, 360))

predictor_path = 'shape_predictor_81_face_landmarks.dat'

detector = dlib.get_frontal_face_detector()#获取面向前面的人脸
predictor = dlib.shape_predictor(predictor_path)#导入训练好的dat模型
lists=[]
while(cap.isOpened()):
    ret, frame = cap.read()
    #frame = cv2.flip(frame, 1)
    if ret==False:
        break
    frame=cv2.resize(frame,(640,360))
    dets = detector(frame, 0)
    another=np.zeros(frame.shape,np.uint8)

    for k, d in enumerate(dets):
        shape = predictor(frame, d)
        landmarks = np.matrix([[p.x, p.y] for p in shape.parts()])#当前特征点的坐标组成的矩阵，2*81维
        lists.append(landmarks)
        #np.savetxt("one.txt",landmarks)
        for num in range(shape.num_parts):
            if(num<17):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (0,255,0), -1)#绿色 从左到右脸部特征点
            elif(num<27):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (0,0,255), -1)#红色 眉毛（左眉前五个，右眉后五个）
            elif(num<31):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (255,0,0), -1)#蓝色 鼻梁
            elif(num<36):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (255,255,0), -1)#青色 鼻翼
            elif(num<48):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (0,255,255), -1)#黄色 眼睛（左眼前6个，右眼后6个）
            elif(num<60):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (255,0,255), -1)#粉红 外嘴唇
            elif(num<68):
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (255,255,255), -1)#白色 内嘴唇
            else:
                cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 1, (100,100,100), -1)#灰色 额头
    cv2.imshow('frame', frame)
    if(not 'shape' in locals().keys()):
        continue
    elif(not type(shape)==np.ndarray):
        shape=shape_to_np(shape)    
    output=visualize_facial_landmarks(frame,shape)
    cv2.imshow("output",output)
    # out.write(frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        print("q pressed")
        break


cap.release()
# out.release()
n=np.array(lists)
np.save('D:\\facial\\shape_predictor_81_face_landmarks-master\\landmarks_731.npy',n)
cv2.destroyAllWindows()



#多特征点方式
#尝试脸上加marker（正脸稳定）
#morph target 表情对应的参数
#feature 驱动blendshape